package org.soar.event;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.common.base.Strings;

public class EventManager {
	
	protected Map<String, List<EventHandler>> registrations = new HashMap<String, List<EventHandler>>();
	
	private static EventManager eventManager = null;
	private EventManager(){};
	
	public static void registerObserver(String eventString, EventListener listener) {
		EventName eventName = parseEventString(eventString);
		Map<String, List<EventHandler>> registrations = getInstance().registrations;
		List<EventHandler> observers = registrations.get(eventName.getName());
		if (observers == null) {
			observers = (List<EventHandler>) Collections.synchronizedList(new ArrayList<EventHandler>());
			registrations.put(eventName.getName(), observers);
		}
		
		observers.add(buildEventHandler(eventName, listener));
	}

	public static void registerObserver(String eventString, final Object instance, final Method method) {
		registerObserver(eventString, new EventListener(){
			@Override
			public void onEvent(Event event, Object o) {
				try {
					method.invoke(instance, event, o);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}
	
	public static void unregisterObserver(String eventString) {
		EventName eventName = parseEventString(eventString);
		Map<String, List<EventHandler>> registrations = getInstance().registrations;
		List<EventHandler> observers = registrations.get(eventName.getName());
		if(observers == null){
			return;
		}
		
		if(Strings.isNullOrEmpty(eventName.getNamespace())){
			observers.clear();
		}else{
			List<EventHandler> removeHandlers = new ArrayList();
			for(EventHandler handler : observers){
				if(eventName.getNamespace().equals(handler.getEventName().getNamespace())){
					removeHandlers.add(handler);
				}
			}
			observers.removeAll(removeHandlers);
		}
		
	}
	
	public static void trigger(String eventString, Object o) {
		EventName eventName = parseEventString(eventString);
		Map<String, List<EventHandler>> registrations = getInstance().registrations;
		List<EventHandler> observers = registrations.get(eventName.getName());
		if(observers == null){
			return;
		}
		
		Event e = buildEvent(eventName);
		
		for(int i = observers.size() - 1; i >= 0 ; i--){
			EventHandler handler = observers.get(i);
			if(!e.getStopPropagation()){
				handler.getEventListener().onEvent(e, o);
			}
		}
	}
	
	public static int getListenerCounts(String eventString) {
		EventName eventName = parseEventString(eventString);
		Map<String, List<EventHandler>> registrations = getInstance().registrations;
		List<EventHandler> observers = registrations.get(eventName.getName());
		if(observers == null){
			return 0;
		}
		
		return observers.size();
	}
	
	
	private synchronized static EventManager getInstance(){
	
		if (eventManager == null) {
			eventManager = new EventManager();
		}

		return eventManager;
	}
	
	private static EventHandler buildEventHandler(EventName eventName, EventListener listener){
		EventHandler eventHandler = new EventHandler();
		eventHandler.setEventName(eventName);
		eventHandler.setEventListener(listener);
		return eventHandler;
	}
	
	private static Event buildEvent(EventName eventName){
		Event event = new Event(eventName.getName());
		return event;
	}
	
	private static EventName parseEventString(String eventString){
		if(Strings.isNullOrEmpty(eventString)){
			throw new RuntimeException("Event name is invalid");
		}
		
		EventName eventName = new EventName();
		String[] names = eventString.split("\\.");
		eventName.setName(names[0]);
		if(names.length > 1){
			eventName.setNamespace(names[1]);
		}
		
		return eventName;
	}

}
